package com.meilai.project.service.business.sale.impl;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.log.Log;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.meilai.project.dto.business.sale.*;
import com.meilai.project.constant.BossUserEnum;
import com.meilai.project.controller.web.market.entity.BillMarket;
import com.meilai.project.controller.web.market.mapper.BillMarketMapper;
import com.meilai.project.controller.web.market.service.BillMarketService;
import com.meilai.project.dto.business.contract.ProxyMediaDetailReq;
import com.meilai.project.dto.workflow.FlowBusinessDataSuperEntity;
import com.meilai.project.dto.workflow.WorkFlowCheckDTO;
import com.meilai.project.entity.business.customer.CustomerInfo;
import com.meilai.project.entity.business.installOrder.WorkOrder;
import com.meilai.project.entity.business.installOrder.WorkerSubmit;
import com.meilai.project.entity.business.installOrder.WorkerSubmitPhoto;
import com.meilai.project.entity.business.intentionScheme.IntentionScheme;
import com.meilai.project.entity.business.sale.*;
import com.meilai.project.entity.business.saleOrder.BillSaleAppendix;
import com.meilai.project.entity.finance.gear.Gear;
import com.meilai.project.entity.finance.paymentReceived.AccountReceivedBillSaleRelation;
import com.meilai.project.entity.finance.reimburse.ReimburseApply;
import com.meilai.project.entity.media.*;
import com.meilai.project.entity.system.MediaTypeSpecPrice;
import com.meilai.project.exception.CommonException;
import com.meilai.project.mapper.business.customer.CustomerInfoMapper;
import com.meilai.project.mapper.business.sale.BillSaleMapper;
import com.meilai.project.mapper.media.MediaPointChooseDaterangeComboCountMapper;
import com.meilai.project.mapper.media.MediaPointChooseDaterangeMapper;
import com.meilai.project.service.business.contract.ProxyMediaDetailService;
import com.meilai.project.service.business.customer.CustomerInfoService;
import com.meilai.project.service.business.installOrder.WorkOrderService;
import com.meilai.project.service.business.installOrder.WorkerSubmitPhotoService;
import com.meilai.project.service.business.installOrder.WorkerSubmitService;
import com.meilai.project.service.business.intentionScheme.IntentionSchemeService;
import com.meilai.project.service.business.intentionScheme.PointProcessService;
import com.meilai.project.service.business.media.MediaPointService;
import com.meilai.project.service.business.sale.*;
import com.meilai.project.service.business.saleOrder.BillSaleAppendixService;
import com.meilai.project.service.finance.paymentReceived.AccountReceivedBillSaleRelationService;
import com.meilai.project.service.finance.reimburse.ReimburseApplyService;
import com.meilai.project.service.gear.GearService;
import com.meilai.project.service.system.MediaTypeSpecPriceService;
import com.meilai.project.service.system.impl.UserServiceImpl;
import com.meilai.project.util.DateRangeType;
import com.meilai.project.util.DateRangeUtil;
import com.meilai.project.util.ThreadLocalUserUtil;
import com.meilai.project.util.WorkFlowUtil;
import com.meilai.project.vo.business.contract.ContractMediaCountDTO;
import com.meilai.project.vo.business.installOrder.Id2PointIdVO;
import com.meilai.project.vo.business.intentionScheme.IntentionSchemeVO;
import com.meilai.project.vo.business.intentionScheme.PointProcessDateRangeVO;
import com.meilai.project.vo.business.media.MediaTypeSpecPriceVO;
import com.meilai.project.vo.business.sale.*;
import com.meilai.project.vo.system.UserForWFVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author xchen
 * @since 2022-03-19
 */
@Slf4j
@Service
public class BillSaleServiceImpl extends ServiceImpl<BillSaleMapper, BillSale> implements BillSaleService {

    @Autowired
    private MediaPointChooseDaterangeComboCountMapper comboCountMapper;

    @Autowired
    private CustomerInfoService customerInfoService;

    @Autowired
    private MediaPointChooseDaterangeMapper daterangeMapper;

    @Autowired
    private CustomerInfoMapper customerInfoMapper;

    @Autowired
    private BillSaleMediaAmountService mediaAmountService;

    @Autowired
    private BillSaleProxyService saleProxyService;

    @Autowired
    private PointProcessService processService;

    @Autowired
    private MediaTypeSpecPriceService priceService;

    @Autowired
    private MediaPointService mediaPointService;

    @Autowired
    private ProxyMediaDetailService proxyMediaDetailService;

    @Autowired
    private IntentionSchemeService intentionSchemeService;

    @Autowired
    private WorkerSubmitService workerSubmitService;

    @Autowired
    private WorkerSubmitPhotoService workerSubmitPhotoService;

    @Autowired
    private WorkOrderService workOrderService;

    @Autowired
    private MediaTypeSpecPriceService mediaTypeSpecPriceService;

    @Autowired
    private BillSaleAppendixService billSaleAppendixService;

    @Autowired
    private BillSaleMediaPriceService billSaleMediaPriceService;

    @Autowired
    private BillSaleMediaCycleService billSaleMediaCycleService;

    @Autowired
    private BillMarketService billMarketService;

    @Autowired
    private AccountReceivedBillSaleRelationService accountReceivedBillSaleRelationService;

    @Autowired
    private ReimburseApplyService reimburseApplyService;

    @Autowired
    private BillSaleGroupRelationService billSaleGroupRelationService;


    @Autowired
    private GearService gearService;

    @Autowired
    private BillSaleGroupService billSaleGroupService;

    @Autowired
    private BillMarketMapper billMarketMapper;

    @Autowired
    private BillSaleMediaDetailService mediaDetailService;

    @Autowired
    private BillSaleProxyMediaDetailService proxyDetailService;

    @Override
    public List<Id2PointIdVO> listBillSaleMediaPoint(List<Long> billSaleIdList) {
        if (CollUtil.isEmpty(billSaleIdList)) {
            return new ArrayList<>();
        }
        return baseMapper.listBillSaleMediaPoint(billSaleIdList);
    }

    @Override
    public List<MediaSpecTypeCountAmountVO> mediaTypeCountAmount(Long dateRangeId) {
        MediaPointChooseDaterange dateRange = daterangeMapper.selectById(dateRangeId);
        List<MediaSpecPriceCountPO> mediaSpecTypeCountList = comboCountMapper.mediaTypeCount(dateRangeId);
        if (CollUtil.isEmpty(mediaSpecTypeCountList)) {
            //正常情况不会为空，调试时候用，可以当这段逻辑没有
            return new ArrayList<>();
        }
        List<Long> mediaTypeSpecPriceIdList = mediaSpecTypeCountList.stream().map(MediaSpecPriceCountPO::getMedia_type_spec_price_id).collect(Collectors.toList());
        List<MediaTypeSpecPrice> mediaTypeSpecPrices = mediaTypeSpecPriceService.listHasMediaTypeId(mediaTypeSpecPriceIdList);
        return assemble(dateRange, mediaTypeSpecPrices, mediaSpecTypeCountList);
    }

    @Override
    public Map<Long, List<MediaSpecTypeCountAmountVO>> mediaTypeCountAmountByIds(List<Long> dateRangeIds) {
        Map<Long, List<MediaSpecTypeCountAmountVO>> result = new HashMap<>();
        Map<Long, MediaPointChooseDaterange> mediaPointChooseDaterangeMap = daterangeMapper.selectBatchIds(dateRangeIds).stream().collect(Collectors.toMap(MediaPointChooseDaterange::getId, Function.identity()));
        List<MediaSpecPriceCountPO> mediaSpecTypeCountList = comboCountMapper.mediaTypeCountByIds(dateRangeIds);
        Map<Long, List<MediaSpecPriceCountPO>> mediaSpecTypeCountMap = mediaSpecTypeCountList.stream().collect(Collectors.groupingBy(MediaSpecPriceCountPO::getDaterange_id));
        List<Long> mediaTypeSpecPriceIdList = mediaSpecTypeCountList.stream().map(MediaSpecPriceCountPO::getMedia_type_spec_price_id).collect(Collectors.toList());
        List<MediaTypeSpecPrice> mediaTypeSpecPrices = mediaTypeSpecPriceService.listHasMediaTypeId(mediaTypeSpecPriceIdList);
        for (Long dateRangeId : dateRangeIds) {
            result.put(dateRangeId, assemble(mediaPointChooseDaterangeMap.get(dateRangeId), mediaTypeSpecPrices, mediaSpecTypeCountMap.get(dateRangeId)));
        }
        return result;
    }

    private List<MediaSpecTypeCountAmountVO> assemble(MediaPointChooseDaterange dateRange, List<MediaTypeSpecPrice> mediaTypeSpecPrices, List<MediaSpecPriceCountPO> mediaSpecPriceCountList) {
        List<MediaSpecTypeCountAmountVO> result = new ArrayList<>();
        Map<Long, MediaTypeSpecPrice> typeSpecPriceMap = mediaTypeSpecPrices.stream().collect(Collectors.toMap(MediaTypeSpecPrice::getId, Function.identity()));
        for (MediaSpecPriceCountPO mediaSpecPriceCountPO : mediaSpecPriceCountList) {
            List<DateRangeType> dateRangeTypes = new ArrayList<>();
            Map<DateRangeType, BigDecimal> type2Price = new HashMap<>();
            Long media_type_spec_price_id = mediaSpecPriceCountPO.getMedia_type_spec_price_id();
            MediaTypeSpecPrice mediaTypeSpecPrice = typeSpecPriceMap.get(media_type_spec_price_id);

            MediaSpecTypeCountAmountVO amountVO = new MediaSpecTypeCountAmountVO();
            if (null != mediaTypeSpecPrice.getPrice_day()) {
                dateRangeTypes.add(DateRangeType.NATURE_DAY);
                type2Price.put(DateRangeType.NATURE_DAY, mediaTypeSpecPrice.getPrice_day());
            }

            if (null != mediaTypeSpecPrice.getPrice_week()) {
                dateRangeTypes.add(DateRangeType.NATURE_WEEK);
                type2Price.put(DateRangeType.NATURE_WEEK, mediaTypeSpecPrice.getPrice_week());
            }

            if (null != mediaTypeSpecPrice.getPrice_month()) {
                dateRangeTypes.add(DateRangeType.NATURE_MONTH);
                type2Price.put(DateRangeType.NATURE_MONTH, mediaTypeSpecPrice.getPrice_month());
            }
            Map<DateRangeType, Integer> dateCountMap = DateRangeUtil.calc(dateRange.getStart_at().toLocalDate(), dateRange.getEnd_at().toLocalDate(), dateRangeTypes);

            amountVO.setMedia_type_spec_price_id(media_type_spec_price_id);
            amountVO.setMedia_type_spec_id(mediaTypeSpecPrice.getMedia_type_spec_id());
            amountVO.setName(mediaSpecPriceCountPO.getMedia_type_spec_price_name());
            amountVO.setSpec_count(mediaSpecPriceCountPO.getSpec_count());
            amountVO.setSize(mediaSpecPriceCountPO.size());
            amountVO.setMedia_type_id(mediaTypeSpecPrice.getMedia_type_id());
            //计算价格
            BigDecimal totalAmount = BigDecimal.ZERO;
            for (DateRangeType dateRangeType : dateRangeTypes) {
                Integer count = dateCountMap.get(dateRangeType);
                BigDecimal price = type2Price.get(dateRangeType);
                totalAmount = totalAmount.add(price.multiply(BigDecimal.valueOf(count)).multiply(BigDecimal.valueOf(mediaSpecPriceCountPO.getSpec_count())));
            }
            amountVO.setAmount(totalAmount);
            amountVO.setHas_price_week(null != mediaTypeSpecPrice.getPrice_week());
            amountVO.setHas_price_day(null != mediaTypeSpecPrice.getPrice_day());
            amountVO.setHas_price_month(null != mediaTypeSpecPrice.getPrice_month());
            // todo 返回给前端单价 (除智能框以外的媒体类型由前端计算价格)
            amountVO.setDay_price(mediaTypeSpecPrice.getPrice_day());
            amountVO.setWeek_price(mediaTypeSpecPrice.getPrice_week());
            amountVO.setMonth_price(mediaTypeSpecPrice.getPrice_month());
            result.add(amountVO);
        }
        return result;
    }

    @Override
    public Page<BillSaleVO> selectPageList(BillSaleListDTO searchDTO) {
        Page<BillSaleVO> page = searchDTO.createPage(BillSaleVO.class);
        List<BillSaleVO> billSaleVOS = baseMapper.selectPageList(page, searchDTO);

        if (CollUtil.isNotEmpty(billSaleVOS)) {
            Map<Long, List<BillSaleVO>> originMap = baseMapper.getChangedVOListByIds(billSaleVOS.stream().map(BillSaleVO::getId).collect(Collectors.toSet()))
                    .stream().collect(Collectors.groupingBy(BillSaleVO::getNewestId));

            // 方案信息
            Set<Long> insIds = billSaleVOS.stream().map(BillSaleVO::getIntention_scheme_id).collect(Collectors.toSet());
            Map<Long, IntentionScheme> insMap = new HashMap<>();
            Map<Long, List<ContractMediaCountDTO>> insMpMap = new HashMap<>();
            if(CollUtil.isNotEmpty(insIds)){
                insMap = intentionSchemeService.listByIds(insIds)
                        .stream().collect(Collectors.toMap(IntentionScheme::getId, Function.identity()));

                insMpMap = baseMapper.getMediaTypeCount(insIds)
                        .stream().collect(Collectors.groupingBy(ContractMediaCountDTO::getId));
            }

            for (BillSaleVO billSaleVO : billSaleVOS) {
                billSaleVO.setFlow_json(null);
                billSaleVO.setOriginList(originMap.getOrDefault(billSaleVO.getId(), new ArrayList<>()));

                billSaleVO.setInsName(Optional.ofNullable(insMap.get(billSaleVO.getIntention_scheme_id())).map(IntentionScheme::getPlan_name).orElse(null));
                billSaleVO.setMediaCountList(insMpMap.getOrDefault(billSaleVO.getIntention_scheme_id(), Collections.emptyList()));
            }
        }
        page.setRecords(billSaleVOS);
        return page;
    }

    @Override
    public List<BillSaleVO> listForMarketByContractId(Long contractId, Long mustIncludeBillSaleId, Integer type) {
        return baseMapper.listForMarketByContractId(contractId, mustIncludeBillSaleId, type);
    }

    @Override
    public List<BillSaleVO> list_by_user_id(Long reimburse_id) {
        Long user_id = ThreadLocalUserUtil.get().getId();
        List<BillSaleVO> voList = baseMapper.list_by_user_id(user_id);
        List<BillSaleVO> result;
        // 过滤已经到款并且没有提交客提的销售单 需要考虑到被退回的销售单回显问题
        List<Long> collect = voList.stream().map(BillSaleVO::getId).collect(Collectors.toList());
        collect.add(-1L);
        // 初次提交 直接过过滤掉没有客提的销售单
        Set<Long> ids = accountReceivedBillSaleRelationService.list(Wrappers.<AccountReceivedBillSaleRelation>lambdaQuery().in(AccountReceivedBillSaleRelation::getBill_sale_id, collect)).stream().map(AccountReceivedBillSaleRelation::getBill_sale_id).collect(Collectors.toSet());
        result = voList.stream().filter(sale -> ids.contains(sale.getId())).collect(Collectors.toList());
        // 继续过滤已经提交客提申请的销售单所在的组中的所有销售单
        List<ReimburseApply> list = reimburseApplyService.list(Wrappers.<ReimburseApply>lambdaQuery().isNotNull(ReimburseApply::getBill_sale_id).and(i -> i.eq(ReimburseApply::getFlow_status, 3).or().eq(ReimburseApply::getFlow_status, 4)).ne(reimburse_id != null, ReimburseApply::getId, reimburse_id));
        // 找出通过审核的客提报销单关联的销售单
        List<Long> bill_sale_ids = list.stream().map(ReimburseApply::getBill_sale_id).collect(Collectors.toList());
        // 找出对应的组中所有的销售单
        if (CollectionUtils.isNotEmpty(bill_sale_ids)) {
            List<Long> filter = billSaleGroupRelationService.getBillSaleIdsByBillSaleIds(bill_sale_ids);
            result = result.stream().filter(item -> !filter.contains(item.getId())).collect(Collectors.toList());
        }
        return result;
    }

    private BillSaleMediaAmountVO mediaSpecTypeCountAmountVO(Long billSaleId, MediaSpecTypeCountAmountVO amountVO) {
        BillSaleMediaAmountVO billSaleMediaAmountVO = new BillSaleMediaAmountVO();
        billSaleMediaAmountVO.setName(amountVO.getName());
        billSaleMediaAmountVO.setBill_sale_id(billSaleId);
        billSaleMediaAmountVO.setMedia_type_spec_price_id(amountVO.getMedia_type_spec_price_id());
        billSaleMediaAmountVO.setMedia_type_spec_id(amountVO.getMedia_type_spec_id());
        billSaleMediaAmountVO.setMedia_count(amountVO.getSpec_count().intValue());
        return billSaleMediaAmountVO;

    }

    /**
     * 当发生锁位修改时，处理媒体形式
     */
    private void processWhenIntentionSchemeModify(List<BillSaleMediaAmountVO> billSaleMediaAmountVOS, BillSaleVO result) {
        List<MediaSpecTypeCountAmountVO> mediaSpecTypeCountAmountVOS = mediaTypeCountAmount(result.getDaterange_id());
        Map<Long, MediaSpecTypeCountAmountVO> priceIdMap = mediaSpecTypeCountAmountVOS.stream().collect(Collectors.toMap(MediaSpecTypeCountAmountVO::getMedia_type_spec_price_id, Function.identity()));
        Iterator<BillSaleMediaAmountVO> iterator = billSaleMediaAmountVOS.iterator();
        while (iterator.hasNext()) {
            BillSaleMediaAmountVO next = iterator.next();
            Long media_type_spec_price_id = next.getMedia_type_spec_price_id();
            MediaSpecTypeCountAmountVO mediaSpecTypeCountAmountVO = priceIdMap.remove(media_type_spec_price_id);
            if (null != mediaSpecTypeCountAmountVO) {
                next.setMedia_count(mediaSpecTypeCountAmountVO.getSpec_count().intValue());
            } else {
                iterator.remove();
            }
        }
        priceIdMap.forEach((k, v) -> {
            BillSaleMediaAmountVO item = mediaSpecTypeCountAmountVO(result.getId(), v);
            billSaleMediaAmountVOS.add(item);
        });
    }

    private void processWhenIntentionSchemeModify(List<BillSaleMediaAmountVO> billSaleMediaAmountVOS, SaleBillVO result) {
        List<MediaSpecTypeCountAmountVO> mediaSpecTypeCountAmountVOS = mediaTypeCountAmount(result.getDaterange_id());
        Map<Long, MediaSpecTypeCountAmountVO> priceIdMap = mediaSpecTypeCountAmountVOS.stream().collect(Collectors.toMap(MediaSpecTypeCountAmountVO::getMedia_type_spec_price_id, Function.identity()));
        Iterator<BillSaleMediaAmountVO> iterator = billSaleMediaAmountVOS.iterator();
        while (iterator.hasNext()) {
            BillSaleMediaAmountVO next = iterator.next();
            Long media_type_spec_price_id = next.getMedia_type_spec_price_id();
            MediaSpecTypeCountAmountVO mediaSpecTypeCountAmountVO = priceIdMap.remove(media_type_spec_price_id);
            if (null != mediaSpecTypeCountAmountVO) {
                next.setMedia_count(mediaSpecTypeCountAmountVO.getSpec_count().intValue());
            } else {
                iterator.remove();
            }
        }
        priceIdMap.forEach((k, v) -> {
            BillSaleMediaAmountVO item = mediaSpecTypeCountAmountVO(result.getId(), v);
            billSaleMediaAmountVOS.add(item);
        });
    }

    @Override
    public BillSaleVO getOneById(Long id) {
        BillSaleVO result = baseMapper.getVOById(id);
        if (result == null) throw new CommonException("数据不存在！");
        Long userId = ThreadLocalUserUtil.get().getId();
        if (null != id) {
            Boolean canCheck = WorkFlowUtil.canCheckData(userId, result);
            result.setCan_check(canCheck);
            result.setIs_applyer(userId.equals(result.getFlow_applyer_id()));
        } else {
            result.setIs_applyer(true);
        }
        IntentionSchemeVO intentionSchemeVO = intentionSchemeService.getOneById(result.getIntention_scheme_id());
        result.setIntention_scheme_related_type(intentionSchemeVO.getRelated_type());
        result.setIntention_scheme_related_id(intentionSchemeVO.getRelated_id());

        //其他信息补充
        List<BillSaleMediaAmountVO> billSaleMediaAmountVOS = mediaAmountService.selectByBillSaleId(id);
        processWhenIntentionSchemeModify(billSaleMediaAmountVOS, result);
        List<Long> mediaTypePriceIdList = billSaleMediaAmountVOS.stream().map(BillSaleMediaAmountVO::getMedia_type_spec_price_id).collect(Collectors.toList());
        List<MediaTypeSpecPriceVO> mediaTypeSpecPriceVOS = priceService.listByIdList(mediaTypePriceIdList);
        Map<Long, List<MediaTypeSpecPriceVO>> mediaTypeMap = mediaTypeSpecPriceVOS.stream().collect(Collectors.groupingBy(MediaTypeSpecPriceVO::getId));
        for (BillSaleMediaAmountVO billSaleMediaAmountVO : billSaleMediaAmountVOS) {
            List<MediaTypeSpecPriceVO> mediaTypeList = mediaTypeMap.get(billSaleMediaAmountVO.getMedia_type_spec_price_id());
            if (CollUtil.isNotEmpty(mediaTypeList)) {
                MediaTypeSpecPriceVO mediaTypeSpecPriceVO = mediaTypeList.get(0);
                billSaleMediaAmountVO.setName(mediaTypeSpecPriceVO.getMedia_type_spec_name());
                billSaleMediaAmountVO.setHas_price_day(mediaTypeSpecPriceVO.getHas_price_day());
                billSaleMediaAmountVO.setHas_price_week(mediaTypeSpecPriceVO.getHas_price_week());
                billSaleMediaAmountVO.setHas_price_month(mediaTypeSpecPriceVO.getHas_price_month());
            }
        }
        result.setMediaAmountVOList(billSaleMediaAmountVOS);

        List<BillSaleProxy> billSaleProxies = saleProxyService.selectByBillSaleId(id);
        result.setBillSaleProxies(billSaleProxies);
        if (CollUtil.isNotEmpty(billSaleProxies)) {
            List<Long> proxyIds = billSaleProxies.stream().map(BillSaleProxy::getId).collect(Collectors.toList());
            Map<Long, List<ProxyMediaDetailReq>> id2ListMap = proxyMediaDetailService.queryByIds(2, proxyIds);
            for (BillSaleProxy billSaleProxy : billSaleProxies) {
                billSaleProxy.setProxyMediaDetailList(id2ListMap.get(billSaleProxy.getId()));
            }
        }


        //点位信息补充
        List<PointProcessDateRangeVO> pointProcessDateRangeVOList = processService.queryChooseDateRangeVOList(result.getChoose_id());
        result.setDateRangeVOList(pointProcessDateRangeVOList);
/*
		List<ProxyMediaDetailReq> proxyMediaDetailReqList = proxyMediaDetailService.query(2, id);
		result.setSaleProxyMediaDetailList(proxyMediaDetailReqList);*/

		/*IntentionScheme intentionScheme = intentionSchemeService.queryByChooseId(result.getChoose_id());
		if (intentionScheme.getRelated_type() == 1) {
			List<ProxyMediaDetailReq> contractMediaDetailDTOList = proxyMediaDetailService.query(1, intentionScheme.getRelated_id());
			result.setContractProxyMediaDetailList(contractMediaDetailDTOList);
		}*/
        // 查询此销售单关联的客户是否是公司客户
        CustomerInfo customerInfo = customerInfoMapper.selectById(result.getCustomerId());
        Long person_in_charge_id = customerInfo.getPerson_in_charge_id();
        // 即是公司客户同时审批人是此客户的负责人时可修改计提百分比字段
        if (result.getCan_check() && BossUserEnum.getEnumById(person_in_charge_id) != null && userId.equals(person_in_charge_id)) {
            result.setUpdate_accrued_amount(true);
        } else {
            result.setUpdate_accrued_amount(false);
        }
//        result.setUpdate_accrued_amount(result.getCan_check() && true);
        // todo 增加媒体类型周期信息
        BillSaleMediaCycle one = billSaleMediaCycleService.getOne(Wrappers.<BillSaleMediaCycle>lambdaQuery().eq(BillSaleMediaCycle::getBill_sale_id, result.getId()));
        result.setBillSaleMediaCycle(one);
        // todo 增加下单时的总价
        List<MediaSpecTypeCountAmountVO> mediaSpecTypeCountAmountVOS = this.mediaTypeCountAmount(result.getDaterange_id());
        List<BillSaleMediaPrice> list = billSaleMediaPriceService.list(Wrappers.<BillSaleMediaPrice>lambdaQuery().eq(BillSaleMediaPrice::getBill_sale_id, result.getId()));
        mediaSpecTypeCountAmountVOS.forEach(item -> {
            List<BillSaleMediaPrice> collect = list.stream().filter(tmp -> Objects.equals(item.getMedia_type_spec_price_id(), tmp.getMedia_type_spec_price_id())).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(collect) && collect.size() == 1) {
                item.setAmount(collect.get(0).getPrice());
            }
        });
        result.setMediaPriceList(mediaSpecTypeCountAmountVOS);
        LocalDate date = LocalDate.of(2022, 9, 20);
        result.setIs_old_data(result.getCreated_at().toLocalDate().isBefore(date));
        result.setIs_company_customer(customerInfoService.getDeteil(result.getCustomerId()).getIs_company_customer());
        return result;
    }

    @Override
    public List<BillSaleVO> getListByIds(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            ids = new ArrayList<>();
            ids.add(-1L);
        }
        List<BillSaleVO> result = baseMapper.getVOListByIds(ids);
        if (CollectionUtils.isNotEmpty(result)) {
            //其他信息补充
            Map<Long, List<BillSaleMediaAmountVO>> billSaleMediaAmountVOMap = mediaAmountService.selectByBillSaleIds(ids);
            Map<Long, List<BillSaleProxy>> proxyMap = saleProxyService.selectByBillSaleIds(ids);
            Map<Long, List<MediaSpecTypeCountAmountVO>> mediaSpecTypeCountAmountVOMap = this.mediaTypeCountAmountByIds(result.stream().map(BillSaleVO::getDaterange_id).collect(Collectors.toList()));
            Map<Long, List<BillSaleMediaPrice>> map = billSaleMediaPriceService.list(Wrappers.<BillSaleMediaPrice>lambdaQuery().in(BillSaleMediaPrice::getBill_sale_id, ids)).stream().collect(Collectors.groupingBy(BillSaleMediaPrice::getBill_sale_id));
            for (BillSaleVO billSaleVO : result) {
                List<BillSaleMediaAmountVO> billSaleMediaAmountVOS = billSaleMediaAmountVOMap.getOrDefault(billSaleVO.getId(), new ArrayList<>());
//                processWhenIntentionSchemeModify(billSaleMediaAmountVOS, billSaleVO);
                billSaleVO.setMediaAmountVOList(billSaleMediaAmountVOS);
                List<BillSaleProxy> billSaleProxies = proxyMap.getOrDefault(billSaleVO.getId(), new ArrayList<>());
                billSaleVO.setBillSaleProxies(billSaleProxies);
                List<BillSaleMediaPrice> list = map.getOrDefault(billSaleVO.getId(), new ArrayList<>());
                List<MediaSpecTypeCountAmountVO> mediaSpecTypeCountAmountVOS = mediaSpecTypeCountAmountVOMap.getOrDefault(billSaleVO.getDaterange_id(), new ArrayList<>());
                mediaSpecTypeCountAmountVOS.forEach(item -> {
                    List<BillSaleMediaPrice> collect = list.stream().filter(tmp -> Objects.equals(item.getMedia_type_spec_price_id(), tmp.getMedia_type_spec_price_id())).collect(Collectors.toList());
                    if (CollectionUtils.isNotEmpty(collect) && collect.size() == 1) {
                        item.setAmount(collect.get(0).getPrice());
                    }
                });
                billSaleVO.setMediaPriceList(mediaSpecTypeCountAmountVOS);
            }
        }
        return result;
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public boolean save(BillSaleDTO dto) {
        int flowResult = saveAndBegin(dto, true);
        if (flowResult != 1) throw new CommonException("保存至草稿箱失败，请联系管理员！");
        else return true;
    }

    public int saveAndBegin(BillSaleDTO dto, boolean isSave) {
        // 获取当前人的id和部门id
        UserForWFVO user = UserServiceImpl.getUserForWF(dto);
        BillSale temp = new BillSale();
        BeanUtils.copyProperties(dto, temp);
        CustomerInfo customerInfo = customerInfoMapper.selectById(dto.getCustomer_id());
        // 设置流程字段，flow_type请自行对应“dict_flow_type”表中的id， 比如当前入职流程（考察登记流程）的id是5
        String flow_title = "销售单申请-" + customerInfo.getName(); // 审批流程的名称，需要根据实际业务自行确定
        FlowBusinessDataSuperEntity fbdse = WorkFlowUtil.getStartSuperDataEntity(user.getUser_id(),  // 当前用户id
                user.getDep_id(), // 提交的部门id
                18, // 流程类型id，“dict_flow_type”表中的id
                flow_title // 自定义的流程标题
        );
        BeanUtils.copyProperties(fbdse, temp);

        // 是否为新数据
        // 新数据时，前端不传业务数据id
        // 非新数据时，即来源是“草稿箱”、“已删除”、“被退回”时，前端需要传入业务数据id
        boolean isFirst = (dto.getId() == null);

        // 由于流程字段含有id，会覆盖数据，此处需要手动setId
        if (!isFirst) temp.setId(dto.getId());


        boolean flag = isFirst ? save(temp) : updateById(temp);
        if (!flag) throw new CommonException("保存失败");
        //新增或更新业务数据
        mediaAmountService.deleteByBillSaleId(temp.getId());
        saleProxyService.deleteByBillSaleId(temp.getId());
        if (CollUtil.isNotEmpty(dto.getBillSaleMediaAmountList())) {
            for (BillSaleMediaAmount billSaleMediaAmount : dto.getBillSaleMediaAmountList()) {
                billSaleMediaAmount.setBill_sale_id(temp.getId());
            }
        }

        if (CollUtil.isNotEmpty(dto.getBillSaleProxyList())) {
            for (BillSaleProxy billSaleProxy : dto.getBillSaleProxyList()) {
                billSaleProxy.setBill_sale_id(temp.getId());
            }
            saleProxyService.saveBatch(dto.getBillSaleProxyList());
            for (BillSaleProxy billSaleProxy : dto.getBillSaleProxyList()) {
                proxyMediaDetailService.saveDetails(2, billSaleProxy.getId(), billSaleProxy.getProxyMediaDetailList());
            }
        }
        mediaAmountService.saveBatch(dto.getBillSaleMediaAmountList());

        // 构造流程工具类
        WorkFlowUtil wfu = new WorkFlowUtil<BillSale, BillSaleMapper>(temp.getId(), user.getUser_id(), BillSale.class);

        // 审批流程id
        // 新数据,传0L
        // 非新数据,根据数据id,查找wfData_id
        Long wfData_id = isFirst ? 0L : wfu.getWfDataId(dto.getId());
        // 旧数据记录留存
        BillSale old_data = isFirst ? new BillSale() : temp;
        // todo 增加写入媒体类型的周期时间 增加写入刊例总价
        // 如果只有智能框类型可能会没有周期数据
        if (dto.getBillSaleMediaCycleDTO() != null) {
            // 清空之前的周期数据
            billSaleMediaCycleService.remove(Wrappers.<BillSaleMediaCycle>lambdaQuery().eq(BillSaleMediaCycle::getBill_sale_id, temp.getId()));
            // 保存新的
            BillSaleMediaCycle cycle = new BillSaleMediaCycle();
            cycle.setBill_sale_id(temp.getId());
            if (CollectionUtils.isNotEmpty(dto.getBillSaleMediaCycleDTO().getCycle_arr())) {
                cycle.setCycle(StringUtils.join(dto.getBillSaleMediaCycleDTO().getCycle_arr(), ","));
            }
            cycle.setMonth(dto.getBillSaleMediaCycleDTO().getCycle_arr().size());
            cycle.setDay(dto.getBillSaleMediaCycleDTO().getDay());
            billSaleMediaCycleService.save(cycle);
        }
        // 清除之前的刊例总价数据
        billSaleMediaPriceService.remove(Wrappers.<BillSaleMediaPrice>lambdaQuery().eq(BillSaleMediaPrice::getBill_sale_id, temp.getId()));
        // 保存新的
        billSaleMediaPriceService.saveBatch(dto.getMediaPriceList().stream().map(item -> {
            BillSaleMediaPrice price = new BillSaleMediaPrice();
            price.setPrice(item.getPrice());
            price.setMedia_type_spec_price_id(item.getMedia_type_spec_price_id());
            price.setBill_sale_id(temp.getId());
            return price;
        }).collect(Collectors.toList()));
        return isSave ? wfu.SaveDraft(wfData_id) : wfu.Begin(wfData_id, "", old_data);
    }
    @Transactional(rollbackFor = Throwable.class)
    @Override
    public boolean begin(BillSaleDTO dto) {
        // 前置检查此方案是否已经被在流程中或者已完成的销售单所使用 如果使用了则不能提交
        List<BillSale> list = this.list(Wrappers.<BillSale>lambdaQuery().eq(BillSale::getIntention_scheme_id, dto.getIntention_scheme_id()).in(BillSale::getFlow_status, 3, 4));
        if (list != null && list.size() > 0) {
            throw new CommonException("此方案已经被其他流程中或者已通过的销售单所占用不能提交");
        }
        // 前置检查此方案是否是锁定状态 不是锁位状态不允许提交
        IntentionScheme intentionScheme = intentionSchemeService.getById(dto.getIntention_scheme_id());
        if (intentionScheme.getType() == 1 && intentionScheme.getProcess_status() != 2) {
            throw new CommonException("此销售单关联的锁位方案未锁定,请先锁定方案");
        }
        // 提交
        int flowResult = saveAndBegin(dto, false);
        if (flowResult == 1) {
            // 仍在流程中
            return true;
        } else if (flowResult == 2) {
            // 无审批节点，直接结束
            endProcess(dto.getId(),true);
            return true;
        } else {
            throw new CommonException("提交失败，请联系管理员！");
        }
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public boolean pass(BillSaleCheckDTO dto) {
        // 通过
        int flowResult = passAndBack(dto, true, false);
        if (flowResult == 1) {
            // 通过后仍然在流程中
            return true;
        } else if (flowResult == 2) {
            // 审核后流程结束
            endProcess(dto.getId(),true);
            return true;
        } else {
            throw new CommonException("通过失败，请联系管理员！");
        }
    }

    public void endProcess(Long id) {
        BillSaleVO saleVO = getOneById(id);
        Long choose_id = saleVO.getChoose_id();
        List<MediaPointChooseDaterangeMediapoint> mediaPointChooseDaterangeMediapoints = processService.listByChooseId(choose_id);
        List<Long> mediaPointIdList = mediaPointChooseDaterangeMediapoints.stream().map(MediaPointChooseDaterangeMediapoint::getMedia_point_id).collect(Collectors.toList());
        List<MediaPoint> mediaPoints = mediaPointService.listByIds(mediaPointIdList);
        List<Long> buildingAreaIdList = mediaPoints.stream().map(MediaPoint::getBuilding_area_id).distinct().collect(Collectors.toList());
        Long intention_scheme_id = saleVO.getIntention_scheme_id();
        IntentionSchemeVO schemeVO = intentionSchemeService.getOneById(intention_scheme_id);


        if (schemeVO.getRelated_type() == 2) {
            BillSaleVO oldBillSale = getOneById(schemeVO.getRelated_id());
            //来自销售单的锁位，是变更类型，要作废原有sale,workOrder单据,拷贝锁位
            processService.invalidByChooseIds(CollUtil.newArrayList(oldBillSale.getChoose_id()));
            Long newUpWorkOrderId = processService.newUpWorkOrder(saleVO, buildingAreaIdList);
            Long newDownWorkOrderId = processService.newDownWorkOrder(saleVO, buildingAreaIdList);
            Long oldBillSaleId = schemeVO.getRelated_id();
            List<WorkOrder> oldWorkOrders = workOrderService.listByBillSaleId(oldBillSaleId);
            for (WorkOrder oldWorkOrder : oldWorkOrders) {
                Long newWorkOrderId = oldWorkOrder.getWork_order_type_id() == 2 ? newUpWorkOrderId : newDownWorkOrderId;
                List<WorkerSubmit> workerSubmits = workerSubmitService.listByWorkOrderId(oldWorkOrder.getId());
                if(CollectionUtils.isEmpty(workerSubmits)) continue;

                // <提交id，照片list>
                Map<Long, List<WorkerSubmitPhoto>> photoMap = workerSubmitPhotoService.list(Wrappers.<WorkerSubmitPhoto>lambdaQuery()
                        .in(WorkerSubmitPhoto::getWorker_submit_id, workerSubmits.stream().map(WorkerSubmit::getId).collect(Collectors.toSet())))
                        .stream().collect(Collectors.groupingBy(WorkerSubmitPhoto::getWorker_submit_id));
                List<WorkerSubmitPhoto> newPhotoList = new ArrayList<>();

                for (WorkerSubmit workerSubmit : workerSubmits) {
                    List<WorkerSubmitPhoto> curPhotoList = photoMap.getOrDefault(workerSubmit.getId(), new ArrayList<>());
                    workerSubmit.setWork_order_id(newWorkOrderId);
                    workerSubmit.setId(null);
                    workerSubmitService.save(workerSubmit);

                    newPhotoList.addAll(curPhotoList.stream().peek(d -> {
                        d.setWorker_submit_id(workerSubmit.getId());
                        d.setId(null);
                    }).collect(Collectors.toList()));
                }
                if(CollectionUtils.isNotEmpty(newPhotoList)) workerSubmitPhotoService.saveBatch(newPhotoList);
            }
            billMarketMapper.updateSaleId(oldBillSale.getId(), id);
        } else {
            // todo 如果销售单是换画类型则生成换画类型的工单
            if (saleVO.getType() == 2) {
                processService.newReplaceWorkOrder(saleVO, buildingAreaIdList);
            } else {
                processService.newUpWorkOrder(saleVO, buildingAreaIdList);
                processService.newDownWorkOrder(saleVO, buildingAreaIdList);
            }
        }
    }

    public void endProcess(Long id,boolean isNew){
        // SaleBillVO saleBillVO = getSingleById(id);
    }


    @Transactional(rollbackFor = Throwable.class)
    @Override
    public boolean returnBack(BillSaleCheckDTO dto) {
        // 驳回
        int flowResult = passAndBack(dto, false, false);
        if (flowResult == 1) { // 如果退回销售单成功
            // todo 然后有关联的市场单
            List<BillMarket> list = billMarketService.list(Wrappers.<BillMarket>lambdaQuery().eq(BillMarket::getBill_sale_id, dto.getId()).isNull(BillMarket::getDeleted_at));
            if (CollectionUtils.isNotEmpty(list)) {
                if (list.size() > 1) {
                    throw new CommonException("销售单关联了多个市场单");
                } else {
                    BillMarket temp = list.get(0);
                    WorkFlowCheckDTO checkDTO = WorkFlowCheckDTO.getWorkFlowCheckDTO(temp.getFlow_current_node_id(), // 当前节点id
                            temp.getFlow_json(), // 流程json
                            "销售单已被退回，市场单强制系统退回",// 审批意见
                            "");
                    WorkFlowUtil<BillMarket, BillMarketMapper> wfu = new WorkFlowUtil<>(temp.getId(), 1L, BillMarket.class);
                    int i = wfu.ReturnBackForce(checkDTO);
                    if (i != 1) {
                        throw new CommonException("驳回失败，请联系管理员！");
                    }
                }
            }
        }
        if (flowResult != 1) throw new CommonException("驳回失败，请联系管理员！");
        return true;
    }

    // 通过 和 驳回 抽出相同代码
    private int passAndBack(BillSaleCheckDTO dto, boolean isPass, boolean... updateData) {
        // 当前人id
        Long userId = ThreadLocalUserUtil.get().getId();

        // 业务数据
        BillSale temp = getById(dto.getId());

        // 构造流程工具类
        WorkFlowUtil wfu = new WorkFlowUtil<BillSale, BillSaleMapper>(dto.getId(), userId, BillSale.class);

        // 审批入参类
        WorkFlowCheckDTO checkDTO = WorkFlowCheckDTO.getWorkFlowCheckDTO(temp.getFlow_current_node_id(), // 当前节点id
                temp.getFlow_json(), // 流程json
                dto.getWf_check_opinion(), // 审批意见
                dto.getWf_check_sign_url(), // 审批签名链接
                JSONObject.toJSONString(temp) // 旧数据留存
        );
        // 如果计提百分比不为空 则更新计提百分比
        if (dto.getAccrued_amount() != null) {
            baseMapper.updateAccruedAmount(dto.getAccrued_amount(), dto.getId());
        }
        // 审批
        return isPass ? wfu.ToNext(checkDTO) : wfu.ReturnBack(checkDTO);
    }


    @Transactional(rollbackFor = Throwable.class)
    @Override
    public boolean delete(Long id) {
        int flowResult = deleteAndActualDel(id, false);
        if (flowResult != 1) throw new CommonException("删除失败，请联系管理员！");
        return true;
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public boolean actualDelete(Long id) {
        int flowResult = deleteAndActualDel(id, true);
        if (flowResult != 1) throw new CommonException("彻底删除失败，请联系管理员！");
        return true;
    }

    @Override
    public void hangUpByChooseIds(List<Long> idList) {
        baseMapper.updateMode(idList, 1);
    }

    @Override
    public void cancelHangUpByChooseIds(List<Long> idList) {
        baseMapper.updateMode(idList, 0);
    }

    @Override
    public void invalidByChooseIds(List<Long> chooseIdList) {
        baseMapper.updateMode(chooseIdList, 2);
    }

    @Override
    public List<BillSale> listByChooseIds(List<Long> chooseIdList) {
        if (CollUtil.isEmpty(chooseIdList)) {
            return new ArrayList<>();
        }
        return baseMapper.listByChooseIds(chooseIdList);
    }

    @Override
    public void update(Long oldContractId, Long newContractId) {
        baseMapper.update(oldContractId, newContractId);
    }

    // 删除 和 彻底删除 抽出相同代码
    private int deleteAndActualDel(Long id, boolean isActual) {
        // 当前人id
        Long userId = ThreadLocalUserUtil.get().getId();
        //释放锁定
        BillSale billSale = baseMapper.selectById(id);
        processService.releaseLock(billSale.getChoose_id());
        // 构造流程工具类
        WorkFlowUtil wfu = new WorkFlowUtil<BillSale, BillSaleMapper>(id, userId, BillSale.class);

        // 流程id
        Long wfData_id = wfu.getWfDataId(id);

        return isActual ? wfu.ActualDelete(wfData_id) : wfu.Delete(wfData_id);
    }

    @Override
    public Page<BillSaleVO2> dispatchList(BillSaleListDTO searchDTO) {
        Page<BillSaleVO2> page = new Page<>(searchDTO.getPage(), searchDTO.getPage_size());
        page.setRecords(baseMapper.dispatchList(page, searchDTO));
        return page;
    }

    @Override
    public Boolean confirmMonitor(Long id) {
        return baseMapper.confirmMonitor(id) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean confirmGroupInvoiceReceive(Long id) {
        if(id == null) throw new CommonException("销售单id不得为空");
        List<BillSaleGroupRelation> groupRelations = billSaleGroupRelationService.list(Wrappers.<BillSaleGroupRelation>lambdaQuery().eq(BillSaleGroupRelation::getBill_sale_id, id));
        if(CollectionUtils.isNotEmpty(groupRelations)) {
            List<BillSaleGroup> groups = billSaleGroupService.list(Wrappers.<BillSaleGroup>lambdaQuery().in(BillSaleGroup::getId,
                    groupRelations.stream().map(BillSaleGroupRelation::getGroup_id).collect(Collectors.toSet())));
            if(CollectionUtils.isNotEmpty(groups)){
                billSaleGroupService.update(Wrappers.<BillSaleGroup>lambdaUpdate()
                        .set(BillSaleGroup::getInvoice_received, 1)
                        .in(BillSaleGroup::getId, groups.stream().map(BillSaleGroup::getId).collect(Collectors.toSet())));
            }
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean uploadAppendix(List<BillSaleAppendix> appendixList) {
        Long bill_sale_id = appendixList.get(0).getBill_sale_id();
        billSaleAppendixService.remove(Wrappers.<BillSaleAppendix>lambdaQuery().eq(BillSaleAppendix::getBill_sale_id, bill_sale_id));
        return billSaleAppendixService.saveBatch(appendixList.stream().map(item -> {
            BillSaleAppendix billSaleAppendix = new BillSaleAppendix();
            billSaleAppendix.setBill_sale_id(bill_sale_id);
            billSaleAppendix.setUrl(item.getUrl());
            billSaleAppendix.setName(item.getName());
            return billSaleAppendix;
        }).collect(Collectors.toList()));
    }

    @Override
    public List<BillSaleAppendix> getAppendixListById(Long id) {
        return billSaleAppendixService.list(Wrappers.<BillSaleAppendix>lambdaQuery().eq(BillSaleAppendix::getBill_sale_id, id));
    }

    @Override
    public List<MediaType> getMediaTypeArrBySaleId(Long id) {
        return baseMapper.getMediaTypeArrBySaleId(id);
    }

    @Override
    public CustomerCommissionVO get_customer_commission_by_sale_id(Long id, BigDecimal amount) {
        // 根据销售和金额返回客提信息
        // step1 查出销售单所在组别中的所有销售单
        List<Long> ids = billSaleGroupRelationService.getBillSaleIdsByBillSaleId(id);
        if (CollectionUtils.isNotEmpty(ids)) {
            CustomerCommissionVO vo = new CustomerCommissionVO();
            List<BillSale> list = this.list(Wrappers.<BillSale>lambdaQuery().in(BillSale::getId, ids));
            BigDecimal total_execute_amount = new BigDecimal("0");
            for (BillSale billSale : list) {
                total_execute_amount = total_execute_amount.add(billSale.getExecute_amount());
            }
            // 算出了执行金额总和以后算刊例总价总和
            List<BillSaleMediaPrice> priceList = billSaleMediaPriceService.list(Wrappers.<BillSaleMediaPrice>lambdaQuery().in(BillSaleMediaPrice::getBill_sale_id, ids));
            BigDecimal total_price = new BigDecimal("0");
            for (BillSaleMediaPrice price : priceList) {
                total_price = total_price.add(price.getPrice());
            }
            vo.setTotal_price(total_price);
            BigDecimal tmp = new BigDecimal(total_execute_amount.toString());
            vo.setTotal_execute_amount(tmp);
            // 计算出折扣
            BigDecimal source_discount = total_execute_amount.divide(total_price, 4, RoundingMode.HALF_UP);
            // 查询出所有的档位
            List<Gear> gear_list = gearService.list(Wrappers.<Gear>lambdaQuery().eq(Gear::getDeleted, false).orderByAsc(Gear::getGear));
            // 从小到大遍历档位 找出档位
            for (Gear gear : gear_list) {
                if (source_discount.compareTo(gear.getTransaction_discount()) < 0) { // 表示是此档位
                    vo.setSource_gear(gear);
                    vo.setMax_quota(total_execute_amount.multiply(gear.getMaintenance_proportion()));
                    break;
                }
            }
            if (vo.getMax_quota() == null && vo.getSource_gear() == null) {// 没有匹配的档位
                throw new CommonException("没有合适的档位 请联系管理员");
            }
            // 判断是否超过最大 客提额度
            if (amount == null || amount.compareTo(vo.getMax_quota()) <= 0) {
                // 没有超过 返回数据
                return vo;
            } else {
                // 计算真实的折扣
                // 计算出超出的部分
                BigDecimal subtract = amount.subtract(vo.getMax_quota());
                total_execute_amount = total_execute_amount.subtract(subtract);
                // 计算出真实的折扣
                BigDecimal real_discount = total_execute_amount.divide(total_price, 4, RoundingMode.HALF_UP);
                for (Gear gear : gear_list) {
                    if (real_discount.compareTo(gear.getTransaction_discount()) < 0) { // 表示是此档位
                        vo.setReal_gear(gear);
                        break;
                    }
                }
                if (vo.getReal_gear() == null) {
                    throw new CommonException("没有符合条件的真实档位 请联系管理员");
                }
                return vo;
            }
        } else {
            throw new CommonException("系统异常");
        }
    }

    @Override
    public boolean updateDate(LocalDate date, List<Long> ids) {
        return baseMapper.updateDate(date, ids) > 0;
    }

    @Override
    public boolean updateReminderDate(LocalDate date, List<Long> ids) {
        return baseMapper.updateReminderDate(date, ids) > 0;
    }

    @Override
    public List<Long> getFullPaymentBillSaleIds() {
        return baseMapper.getFullPaymentBillSaleIds();
    }

    @Override
    public Boolean relation(BillSaleGroupDTO dto, Long user_id) {
        if (CollectionUtils.isEmpty(dto.getBill_sale_ids()) || StringUtils.isEmpty(dto.getName())) {
            throw new CommonException("提交的数据异常，请联系管理员");
        } else {
            // 前置检查销售单是否来源于同一个合同
            List<BillSale> billSales = baseMapper.selectList(Wrappers.<BillSale>lambdaQuery().in(BillSale::getId, dto.getBill_sale_ids()));
            if (billSales.stream().map(BillSale::getContract_id).collect(Collectors.toSet()).size() > 1) {
                throw new CommonException("所选销售单中不是同一个合同，请检查提交的销售单数据");
            }
            List<BillSaleGroupRelation> groupRelationList = billSaleGroupRelationService.list(Wrappers.<BillSaleGroupRelation>lambdaQuery().select(BillSaleGroupRelation::getId).in(BillSaleGroupRelation::getBill_sale_id, dto.getBill_sale_ids()));
            if(groupRelationList.size() > 0) throw new CommonException("所选销售单中已新建了分组名称");
            BillSaleGroup group = new BillSaleGroup();
            group.setCreated_by(ThreadLocalUserUtil.get().getId());
            group.setName(dto.getName());
            group.setCreated_at(LocalDateTime.now());
            group.setCreated_by(user_id);
            billSaleGroupService.save(group);
            List<BillSaleGroupRelation> billSaleGroupRelationList = new ArrayList<>();
            dto.getBill_sale_ids().forEach(item -> {
                BillSaleGroupRelation relation = new BillSaleGroupRelation();
                relation.setBill_sale_id(item);
                relation.setGroup_id(group.getId());
                billSaleGroupRelationList.add(relation);
            });
            billSaleGroupRelationService.saveBatch(billSaleGroupRelationList);
            return true;
        }
    }

    @Override
    public List<BillSaleGroup> listGroup(Long user_id) {
        return billSaleGroupService.listGroup(user_id);
    }

    /*======================================================NEW================================================*/
    @Override
    public List<SaleBillVO> listLatestSaleBill(Long deptId, Long contractId, Long mustIncludeBillSaleId, Integer type){
        List<SaleBillVO> saleBillList = baseMapper.listLatestSaleBill(deptId, contractId, mustIncludeBillSaleId, type);
        saleBillList.forEach(result -> {
            // 自营媒体
            List<BillSaleMediaDetailVO> mediaDetailList = mediaDetailService.selectByBillSaleId(result.getId());
            if( mediaDetailList.size() > 0 ){
                result.setMediaDetailList( mediaDetailList );
            }
            // 代理媒体
            List<BillSaleProxyMediaDetailVO> proxyMediaDetailList = proxyDetailService.selectByBillSaleId(result.getId());
            if( proxyMediaDetailList.size() > 0 ){
                result.setProxyMediaDetailList( proxyMediaDetailList );
            }
        });
        return saleBillList;
    }
    public Long saveAndBegin(SaleBillDTO dto, boolean isSave) {
        // 获取当前人的id和部门id
        UserForWFVO user = UserServiceImpl.getUserForWF(dto);
        BillSale temp = new BillSale();
        BeanUtils.copyProperties(dto, temp);
        CustomerInfo customerInfo = customerInfoMapper.selectById(dto.getCustomer_id());
        // 设置流程字段，flow_type请自行对应“dict_flow_type”表中的id， 比如当前入职流程（考察登记流程）的id是5
        String flow_title = "销售单申请-" + customerInfo.getName(); // 审批流程的名称，需要根据实际业务自行确定
        FlowBusinessDataSuperEntity fbdse = WorkFlowUtil.getStartSuperDataEntity(user.getUser_id(),  // 当前用户id
                user.getDep_id(), // 提交的部门id
                18, // 流程类型id，“dict_flow_type”表中的id
                flow_title // 自定义的流程标题
        );
        BeanUtils.copyProperties(fbdse, temp);
        // 是否为新数据
        // 新数据时，前端不传业务数据id
        // 非新数据时，即来源是“草稿箱”、“已删除”、“被退回”时，前端需要传入业务数据id
        boolean isFirst = (dto.getId() == null);

        // 由于流程字段含有id，会覆盖数据，此处需要手动setId
        if (!isFirst) temp.setId(dto.getId());

        boolean flag = isFirst ? save(temp) : updateById(temp);
        if (!flag) throw new CommonException("保存失败");

        mediaDetailService.deleteByBillSaleId(temp.getId());
        proxyDetailService.deleteByBillSaleId(temp.getId());

        // 自营媒体
        List<BillSaleMediaDetail> mediaDetailList = dto.getMediaDetailList();
        if (CollUtil.isNotEmpty(mediaDetailList)) {
            mediaDetailList.stream().map(item -> {
                item.setContract_id(temp.getContract_id());
                item.setBill_sale_id(temp.getId());
                return item;
            }).collect(Collectors.toList());
            mediaDetailService.saveBatch( mediaDetailList );
        }
        // 代理媒体
        List<BillSaleProxyMediaDetail> proxyMediaDetailList = dto.getProxyMediaDetailList();
        if (CollUtil.isNotEmpty(proxyMediaDetailList)) {
            proxyMediaDetailList.stream().map(item -> {
                item.setContract_id(temp.getContract_id());
                item.setBill_sale_id(temp.getId());
                return item;
            }).collect(Collectors.toList());
            proxyDetailService.saveBatch( proxyMediaDetailList );
        }

        // 构造流程工具类
        WorkFlowUtil wfu = new WorkFlowUtil<BillSale, BillSaleMapper>(temp.getId(), user.getUser_id(), BillSale.class);
        // 审批流程id
        // 新数据,传0L
        // 非新数据,根据数据id,查找wfData_id
        Long wfData_id = isFirst ? 0L : wfu.getWfDataId(dto.getId());
        // 旧数据记录留存
        BillSale old_data = isFirst ? new BillSale() : temp;
        // 保存流程
        int flowResult = isSave ? wfu.SaveDraft(wfData_id) : wfu.Begin(wfData_id, "", old_data);
        if ( isSave ){
            if( flowResult != 1 ){
                throw new CommonException("保存至草稿箱失败，请联系管理员！");
            }
        }else {
            if (flowResult == 1) {// 仍在流程中
            } else if (flowResult == 2) {
                // 无审批节点，直接结束
                endProcess(dto.getId(),true);
            } else {
                throw new CommonException("提交失败，请联系管理员！");
            }
        }
        // 如果是1 则返回1 不是则返回当前销售单ID
        return temp.getId();
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public Long save(SaleBillDTO dto) {
        return saveAndBegin(dto, true);
    }

    @Override
    public Long begin(SaleBillDTO dto) {
        // 提交
        return saveAndBegin(dto, false);
    }

    public SaleBillVO getSingleById(Long id) {
        // 获取当前销售单
        SaleBillVO result = baseMapper.getSingleVOById( id );
        if (result == null) throw new CommonException("数据不存在！");
        Long userId = ThreadLocalUserUtil.get().getId();
        if (null != id) {
            Boolean canCheck = WorkFlowUtil.canCheckData(userId, result);
            result.setCan_check(canCheck);
            result.setIs_applier(userId.equals(result.getFlow_applyer_id()));
        } else {
            result.setIs_applier(true);
        }

        // 自营媒体
        List<BillSaleMediaDetailVO> mediaDetailList = mediaDetailService.selectByBillSaleId( id );
        if( mediaDetailList.size() > 0 ){
            mediaDetailList.stream().map(item ->{
                item.setDuration(new String[]{item.getExecute_time_start().toString(),item.getExecute_time_end().toString()});
                return item;
            }).collect(Collectors.toList());
            result.setMediaDetailList( mediaDetailList );
        }

        // 代理媒体
        List<BillSaleProxyMediaDetailVO> proxyMediaDetailList = proxyDetailService.selectByBillSaleId(id);
        if( proxyMediaDetailList.size() > 0 ){
            proxyMediaDetailList.stream().map(item ->{
                item.setDuration(new String[]{item.getExecute_time_start().toString(),item.getExecute_time_end().toString()});
                return item;
            }).collect(Collectors.toList());
            result.setProxyMediaDetailList( proxyMediaDetailList );
        }

        // 查询此销售单关联的客户是否是公司客户
        CustomerInfo customerInfo = customerInfoMapper.selectById(result.getCustomerId());
        Long person_in_charge_id = customerInfo.getPerson_in_charge_id();
        // 即是公司客户同时审批人是此客户的负责人时可修改计提百分比字段
        if (result.getCan_check() && BossUserEnum.getEnumById(person_in_charge_id) != null && userId.equals(person_in_charge_id)) {
            result.setUpdate_accrued_amount(true);
        } else {
            result.setUpdate_accrued_amount(false);
        }

        LocalDate date = LocalDate.of(2022, 9, 20);
        result.setIs_old_data(result.getCreated_at().toLocalDate().isBefore(date));
        result.setIs_company_customer(customerInfoService.getDeteil(result.getCustomerId()).getIs_company_customer());
        return result;
    }

    @Override
    public Page<SaleBillVO> selectNewPageList(SaleBillListDTO searchDTO) {
        Page<SaleBillVO> page = searchDTO.createPage(SaleBillVO.class);
        List<SaleBillVO> saleBillList = baseMapper.selectNewPageList(page, searchDTO);

        page.setRecords(saleBillList);
        return page;
    }

    @Override
    public BigDecimal getTotalExecutedAmount(Long contract_id) {
        return baseMapper.sumExecutedMoney(contract_id);
    }

}
